/*
 * Copyright (C) 2008
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.sca4idea.providers;

import com.googlecode.sca4idea.facet.SCAFacet;
import com.intellij.openapi.project.Project;
import com.intellij.patterns.ElementPattern;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceProvider;
import com.intellij.psi.PsiReferenceRegistrar;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;

/**
 * Implementation of the ProviderReferenceRegistrar.
 */
public class ProviderReferenceRegistrarImpl implements ProviderReferenceRegistrar {
    private final String providerId;
    private final PsiReferenceRegistrar registry;

    /**
     * Creates a new registrar associated with the given provider.
     * @param providerId the provider id
     * @param registry the registry
     */
    public ProviderReferenceRegistrarImpl(String providerId, PsiReferenceRegistrar registry) {
        this.providerId = providerId;
        this.registry = registry;
    }

    public Project getProject() {
        return registry.getProject();
    }

    public void registerReferenceProvider(final @NotNull ElementPattern<? extends PsiElement> pattern,
                                          @NotNull PsiReferenceProvider referenceProvider) {
        // Register the pattern
        registry.registerReferenceProvider(pattern, new ReferenceProviderWrapper(referenceProvider));
    }

    private class ReferenceProviderWrapper extends PsiReferenceProvider {
        private final PsiReferenceProvider underlying;

        private ReferenceProviderWrapper(PsiReferenceProvider underlying) {
            this.underlying = underlying;
        }

        @NotNull
        public PsiReference[] getReferencesByElement(@NotNull PsiElement psiElement,
                                                     @NotNull ProcessingContext processingContext) {
            if (applies(psiElement)) {
                return underlying.getReferencesByElement(psiElement, processingContext);
            }

            return new PsiReference[0];
        }

        private boolean applies(PsiElement element) {
            SCAProvider provider = SCAFacet.getProviderForElement(element);
            while (provider != null) {
                if (provider.getId().equals(providerId)) {
                    return true;
                }

                provider = provider.getInheritsFrom();
            }
            
            return false;
        }
    }
}
